package main

import (
	"encoding/json"
	"fmt"
	"net"
	"os"
	"runtime"

	"github.com/containernetworking/cni/pkg/skel"
	"github.com/containernetworking/cni/pkg/types"
	types040 "github.com/containernetworking/cni/pkg/types/040"
	"github.com/containernetworking/cni/pkg/version"
	"github.com/vishvananda/netlink"
)

const (
	BridgeName    = "cni0"
	Subnet        = "10.244.0.0/24"
	GatewayIP     = "10.244.0.1"
	ContainerIP   = "10.244.0.100/24"
	InterfaceName = "eth0"
)

type NetConf struct {
	types.NetConf
	Bridge string `json:"bridge"`
}

func init() {
	runtime.LockOSThread()
}

func main() {
	skel.PluginMain(cmdAdd, cmdCheck, cmdDel, version.All, "")
}

func cmdAdd(args *skel.CmdArgs) error {
	conf := NetConf{}
	if err := json.Unmarshal(args.StdinData, &conf); err != nil {
		return fmt.Errorf("failed to parse config: %v", err)
	}

	if err := setupBridge(conf.Bridge); err != nil {
		return err
	}

	hostVeth, containerVeth, err := createVethPair(args.ContainerID, args.IfName)
	if err != nil {
		return err
	}

	if err := moveToContainerNS(args.Netns, containerVeth); err != nil {
		return err
	}

	result, err := configureContainerNetwork(args.Netns, containerVeth)
	if err != nil {
		return err
	}

	if err := attachHostVethToBridge(hostVeth, conf.Bridge); err != nil {
		return err
	}

	return types.PrintResult(result, conf.CNIVersion)
}

func cmdDel(args *skel.CmdArgs) error {
	hostVethName := "veth" + args.ContainerID[:11]
	if link, err := netlink.LinkByName(hostVethName); err == nil {
		_ = netlink.LinkDel(link)
	}
	return nil
}

func cmdCheck(args *skel.CmdArgs) error {
	return nil
}

// 核心网络配置函数
func setupBridge(bridgeName string) error {
	if _, err := netlink.LinkByName(bridgeName); err != nil {
		la := netlink.NewLinkAttrs()
		la.Name = bridgeName
		br := &netlink.Bridge{LinkAttrs: la}
		if err := netlink.LinkAdd(br); err != nil {
			return fmt.Errorf("create bridge failed: %v", err)
		}
		if err := netlink.LinkSetUp(br); err != nil {
			return err
		}
	}
	return nil
}

func createVethPair(containerID, ifName string) (netlink.Link, netlink.Link, error) {
	vethName := "veth" + containerID[:11]
	veth := &netlink.Veth{
		LinkAttrs: netlink.LinkAttrs{Name: vethName},
		PeerName:  ifName,
	}
	if err := netlink.LinkAdd(veth); err != nil {
		return nil, nil, fmt.Errorf("create veth pair failed: %v", err)
	}

	hostVeth, _ := netlink.LinkByName(vethName)
	containerVeth, _ := netlink.LinkByName(ifName)

	return hostVeth, containerVeth, nil
}

func moveToContainerNS(nsPath string, link netlink.Link) error {
	f, err := os.OpenFile(nsPath, os.O_RDONLY, 0)
	if err != nil {
		return fmt.Errorf("open netns %q failed: %v", nsPath, err)
	}
	defer f.Close()

	if err := netlink.LinkSetNsFd(link, int(f.Fd())); err != nil {
		return fmt.Errorf("move veth to container ns failed: %v", err)
	}
	return nil
}

func configureContainerNetwork(nsPath string, link netlink.Link) (types.Result, error) {
	_, ipNet, _ := net.ParseCIDR(ContainerIP)
	gw := net.ParseIP(GatewayIP)

	err := nsDo(nsPath, func() error {
		if err := netlink.LinkSetUp(link); err != nil {
			return err
		}

		addr := &netlink.Addr{IPNet: ipNet}
		if err := netlink.AddrAdd(link, addr); err != nil {
			return fmt.Errorf("add IP address failed: %v", err)
		}

		route := netlink.Route{
			Dst: nil,
			Gw:  gw,
		}
		return netlink.RouteAdd(&route)
	})

	if err != nil {
		return nil, err
	}

	return &types040.Result{
		CNIVersion: types040.ImplementedSpecVersion,
		Interfaces: []*types040.Interface{
			{
				Name:    InterfaceName,
				Mac:     link.Attrs().HardwareAddr.String(),
				Sandbox: nsPath,
			},
		},
		IPs: []*types040.IPConfig{
			{
				Address:   *ipNet,
				Gateway:   gw,
				Interface: types040.Int(0),
			},
		},
	}, nil
}

func nsDo(nsPath string, fn func() error) error {
	// 简化实现，实际需要处理命名空间切换
	return fn()
}

func attachHostVethToBridge(hostVeth netlink.Link, bridgeName string) error {
	br, err := netlink.LinkByName(bridgeName)
	if err != nil {
		return err
	}

	if err := netlink.LinkSetMaster(hostVeth, br); err != nil {
		return fmt.Errorf("add veth to bridge failed: %v", err)
	}

	return netlink.LinkSetUp(hostVeth)
}
